#ifndef _M_QUEUE_H_
#define _M_QUEUE_H_

#include"../mqcommon/mq_helper.hpp"
#include"../mqcommon/mq_logger.hpp"
#include"../mqcommon/mq_msg.pb.h"
#include<iostream>
#include<unordered_map>
#include<mutex>
#include<memory>

namespace xiang11mq
{
//1.定义队列描述数据类
struct MsgQueue
{
    using ptr = std::shared_ptr<MsgQueue>;
    MsgQueue(){}
    MsgQueue(const std::string &qname, bool qdurable, bool qexclusize, bool qauto_delete
        ,const google::protobuf::Map<std::string, std::string> &qargs)
        :name(qname)
        ,durable(qdurable)
        ,exclusive(qexclusize)
        ,auto_delete(qauto_delete) 
        ,args(qargs)
    {}

    std::string name;
    bool durable;
    bool exclusive;     //是否独占
    bool auto_delete;
    google::protobuf::Map<std::string, std::string> args;

    void setArgs(const std::string &str_args)
    {
        // key=val&key=val
        std::vector<std::string> sub_args;
        StrHelper::split(str_args, "&", sub_args);
        for (auto &str : sub_args)
        {
            size_t pos = str.find("=");
            if (pos == std::string::npos)
            {
                ELOG("MsgQueue 解析str_args 出现错误");
            }
            std::string key = str.substr(0, pos);
            std::string val = str.substr(pos + 1);
            args.insert({key, val});
        }
    }
    // 将args的内容序列化之后 返回一个字符串
    std::string getArgs()
    {
        std::string result;
        for (auto [x, y] : args)
        {
            result += x + "=" + y + "&";
        }
        return result;
    }
};
using QueueMap = std::unordered_map<std::string, MsgQueue::ptr>;

//2.定义队列数据持久化类（数据持久化的sqlite3数据库中）
class MsgQueueMapper
{
public:
    MsgQueueMapper(const std::string &dbfile)
        :_sql_helper(dbfile)
    {
        std::string path = FileHelper::parentDirectory(dbfile);
        FileHelper::createDirectory(path);
        assert(_sql_helper.open());
        createTable();
    }
    //创建消息队列表
    void createTable()
    {
        #define MSGQUEUE_CREATE_TABLE "create table if not exists queue_table(       \
            name varchar(32) primary key,                                   \
            durable int,                                                    \
            exclusive int,                                                  \
            auto_delete int,                                                \
            args varchar(128)                                               \
        );"
        bool ret = _sql_helper.execute(MSGQUEUE_CREATE_TABLE, nullptr, nullptr);
        if(ret == false)
        {
            DLOG("创建消息队列表失败");
            abort();                    //异常退出
        }
    }
    //删除消息队列表
    void removeTable()
    {
        #define MSGQUEUE_DROP_TABLE "drop table if exists queue_table;"
        bool ret = _sql_helper.execute(MSGQUEUE_DROP_TABLE, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("删除消息队列表失败");
            abort(); // 异常退出
        }
    }
    //新增消息队列数据
    bool insert(MsgQueue::ptr &mqp)
    {
        #define MSGQUEUE_INSERT_SQL "insert into queue_table values('%s', %d, %d, %d, '%s');"
        char sql_str[255] = {0};
        sprintf(sql_str, MSGQUEUE_INSERT_SQL, mqp->name.c_str(), (int)mqp->durable, (int)mqp->exclusive, (int)mqp->auto_delete, mqp->getArgs().c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("新增消息队列表数据失败");
            return false;
        }
        return true;
    }
    //删除消息队列数据
    void remove(const std::string &name)
    {
        #define MSGQUEUE_DELETE_SQL "delete from queue_table where name = '%s';"
        char sql_str[255] = {0};
        sprintf(sql_str, MSGQUEUE_DELETE_SQL, name.c_str());
        bool ret = _sql_helper.execute(sql_str, nullptr, nullptr);
        if (ret == false)
        {
            DLOG("删除消息队列表数据失败");
        }

    }
    //获取所有队列信息
    QueueMap recovery()
    {
        QueueMap result;
        #define MSGQUEUE_SELECT_SQL "select name, durable, exclusive, auto_delete, args from queue_table;" 
        bool ret = _sql_helper.execute(MSGQUEUE_SELECT_SQL, selectCallback, &result);
        if(ret == 0)
        {
            ELOG("数据恢复失败");
            abort();
        }
        return result;
    }
private:
    // typedef int (*SqliteCallback)(void*, int, char**, char**)
    static int selectCallback(void *arg, int numcol, char **row, char **fileds)
    {
        QueueMap *result = (QueueMap*) arg;
        auto mqp = std::make_shared<MsgQueue>();
        mqp->name = row[0];
        mqp->durable = (bool)std::stoi(row[1]);
        mqp->exclusive = (bool)std::stoi(row[2]);;
        mqp->auto_delete = (bool)std::stoi(row[3]);
        if(row[4])
            mqp->setArgs(row[4]);
        result->insert(std::make_pair(mqp->name, mqp));
        return 0;       //必须有
    }
private:
    SqliteHelper _sql_helper;
};
//3.定义队列数据管理类
class MsgQueueManager
{
public:
    using ptr = std::shared_ptr<MsgQueueManager>;
    MsgQueueManager(const std::string &dbfile)
        :_mapper(dbfile)
    {
        _msg_queues = _mapper.recovery();
    }
    //声明消息队列 若必要添加存储
    bool declareQueue(const std::string &qname, bool qdurable, bool qexclusive, bool qauto_delete
        ,const google::protobuf::Map<std::string, std::string> &qargs)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        //查询消息队列是否存在    由于exists也要加锁 不能加同一把锁 所以自己判断
        auto it = _msg_queues.find(qname);
        if(it != _msg_queues.end())
        {
            //如果消息队列存在 直接返回
            DLOG("要添加的消息队列已存在");
            return false;
        }
        auto mqp = std::make_shared<MsgQueue>(qname, qdurable, qexclusive, qauto_delete, qargs);
        //判断是否需要持久化存储tongyibasuo
        if(qdurable == true)
        {
            bool ret =_mapper.insert(mqp);
            if(ret == false)
            {
                return false;
            }
        }
        _msg_queues.insert(make_pair(mqp->name, mqp));
        return true;
    }
    //删除一个消息队列
    void deleteQueue(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _msg_queues.find(name);
        if(it == _msg_queues.end())
        {
            //如果消息队列存在 直接返回
            DLOG("要删除的消息队列不存在");
            return;
        }
        if(it->second->durable == true)
        {
            _mapper.remove(name);
            //DLOG("删除消息队列表数据%s成功", name.c_str());
        }

        _msg_queues.erase(name);
    }
    //获取指定消息队列
    MsgQueue::ptr selectQueue(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _msg_queues.find(name);
        if(it == _msg_queues.end())
        {
            //如果消息队列存在 直接返回
            DLOG("要获取的消息队列不存在");
            return MsgQueue::ptr();
        }
        return it->second;
    }
    //获取所有队列信息
    QueueMap allQueues()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _msg_queues;     //返回临时对象 防止脱离类 出现线程安全问题
    }
    //查询消息队列是否存在
    bool exists(const std::string &name)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _msg_queues.find(name);
        if(it == _msg_queues.end())
        {
            return false;
        }
        return true;
    }
    //返回消息队列数量
    size_t size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _msg_queues.size();
    }
    //删除所有消息队列数据
    void clear()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _mapper.removeTable();
        _msg_queues.clear();
    }

private:
    std::mutex _mutex;
    MsgQueueMapper _mapper;         //持久化的map
    QueueMap _msg_queues;           //所有消息队列的map
};


}

#endif